#include "VulkanContext.h"
#include "VulkanBarriers.h"
#include "VulkanSemaphore.h"
#include "VulkanDevice.h"
#include "VulkanCommandBuffer.h"
#include "VulkanPipeline.h"
#include "VulkanViewport.h"
#include "VulkanRenderPass.h"
#include "VulkanPendingState.h"
#include "Runtime/CGI/CGIRenderTarget.h"

namespace Alice
{
    VulkanCommandListContext::VulkanCommandListContext(CGIVulkan* InRHI, VulkanDevice* InDevice, VulkanQueue* InQueue, VulkanCommandListContext* InImmediate)
        :mRHI(InRHI),
        mDevice(InDevice),
        mQueue(InQueue),
        mImmediate(InImmediate)
    {
        mCommandBufferManager = new CGIVulkanCommandBufferManager(InDevice, this);
        mCommandBufferManager->Init(this);
        mPendingGraphicsState=new VulkanPendingGraphicsState(InDevice,this);
    }
    VulkanRenderPass* VulkanCommandListContext::PrepareRenderPassForPSOCreation(const FGraphicsPipelineStateInitializer& Initializer)
    {
        VulkanRenderTargetLayout RTLayout(Initializer);
        VulkanRenderPass* RenderPass = nullptr;
        RenderPass = mDevice->mRenderPassManager->GetOrCreateRenderPass(RTLayout);
        return RenderPass;
    }
    void VulkanCommandListContext::RHIBeginDrawingViewport(CGIViewport*inViewport,CGITexture*inCGITexture)
    {
        VulkanViewport*viewport=(VulkanViewport*)inViewport;
        
    }
    void VulkanCommandListContext::RHIEndDrawingViewport(CGIViewport* inCGIViewport, bool bPresent, bool bLockToVsync)
    {
        VulkanViewport*viewport=reinterpret_cast<VulkanViewport*>(inCGIViewport);
        VulkanCommandBuffer* commandBuffer=mCommandBufferManager->mActiveCmdBuffer;
        viewport->Present(this,commandBuffer,mQueue,mDevice->mPresentQueue,true);
    }

    void VulkanCommandListContext::RHIBeginRenderPass(const CGIRenderPassInfo* InInfo, const TCHAR* InName)
    {
        VkImageLayout CurrentDepthLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        VkImageLayout CurrentStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        VulkanRenderTargetLayout RTLayout(*mDevice, InInfo, CurrentDepthLayout, CurrentStencilLayout);
        VulkanRenderPass* renderPass = mDevice->mRenderPassManager->GetOrCreateRenderPass(RTLayout);
        VulkanCommandBuffer* commandBuffer=mCommandBufferManager->mActiveCmdBuffer;
        commandBuffer->Begin();
        const VkImageSubresourceRange Range = CGIVulkanPipelineBarrier::MakeSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
        CGIVulkanTexture* rtTexture=reinterpret_cast<CGIVulkanTexture*>(InInfo->mColorRenderTargets.RenderTarget);
        VulkanSetImageLayout(commandBuffer, rtTexture->mImage, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, Range);
        VkClearValue AttachmentClearValues[2];
        AttachmentClearValues[0].color = {0.1f,0.4f,0.6f,1.0f};
        AttachmentClearValues[1].depthStencil = {1.0f,0u};
        CGISetRenderTargetsInfo RTInfo;
        InInfo->ConvertToRenderTargetsInfo(RTInfo);
        VulkanFramebuffer*frameBuffer=mDevice->mRenderPassManager->GetOrCreateFramebuffer(RTInfo,RTLayout,renderPass);
        VkRenderPassBeginInfo Info={};
        Info.sType=VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
        Info.renderPass = renderPass->GetHandle();
        Info.framebuffer = frameBuffer->GetHandle();
        Info.renderArea = frameBuffer->GetRenderArea();
        Info.clearValueCount = 2;
        Info.pClearValues = AttachmentClearValues;
        vkCmdBeginRenderPass(commandBuffer->GetHandle(), &Info, VK_SUBPASS_CONTENTS_INLINE);
        mCurrentRenderPass=renderPass;
        mCurrentFramebuffer=frameBuffer;
    }
    void VulkanCommandListContext::RHIEndRenderPass()
    {
        VulkanCommandBuffer* CmdBuffer = mCommandBufferManager->mActiveCmdBuffer;
        
        //mDevice->mRenderPassManager->EndRenderPass(CmdBuffer);
        vkCmdEndRenderPass(CmdBuffer->GetHandle());
        CmdBuffer->State = VulkanCommandBuffer::EState::IsInsideBegin;
        mCurrentRenderPass = nullptr;
        mPendingGraphicsState->mVulkanCGIGraphicPipelineState=nullptr;
    }

    void VulkanCommandListContext::RHISetGraphicsPipelineState(CGIGraphicsPipelineState* GraphicsState, uint32 StencilRef, bool bApplyAdditionalState)
    {
        FVulkanCGIGraphicPipelineState*pipeline=(FVulkanCGIGraphicPipelineState*)GraphicsState;
        const VulkanCommandBuffer*commandBuffer=mCommandBufferManager->mActiveCmdBuffer;
        if(mPendingGraphicsState->SetGraphicsPipeline(pipeline,false))
        {
            mPendingGraphicsState->Bind(commandBuffer->GetHandle());
        }
    }
    void VulkanCommandListContext::RHISetStreamSource(uint32 StreamIndex, CGIBuffer* VertexBuffer, uint32 Offset)
    {
        VulkanCommandBuffer*cmdBuffer=mCommandBufferManager->mActiveCmdBuffer;
        VulkanResourceMultiBuffer*vulkanBuffer=(VulkanResourceMultiBuffer*)VertexBuffer;
        VkBuffer buffer=vulkanBuffer->mAlloc.Alloc.GetBufferHandle();
        VkDeviceSize offsets[] = {0};
        vkCmdBindVertexBuffers(cmdBuffer->mCommandBuffer, 0, 1, &buffer, offsets);
    }
    void VulkanCommandListContext::RHIDrawPrimitive(uint32 BaseVertexIndex, uint32 NumPrimitives, uint32 NumInstances)
    {
        VulkanCommandBuffer*cmdBuffer=mCommandBufferManager->mActiveCmdBuffer;
        vkCmdDraw(cmdBuffer->mCommandBuffer, 3, 1, 0, 0);
    }
    CGIVulkanCommandListContextImmediate::CGIVulkanCommandListContextImmediate(CGIVulkan* InRHI, VulkanDevice* InDevice, VulkanQueue* InQueue):
        VulkanCommandListContext(InRHI,InDevice,InQueue,this)
    {
        
    }
}
